import {Component, OnInit} from '@angular/core';
import {UploadFileService} from './upload-file.service';
import {CommonTool} from '../common.tool';
import {NzMessageService} from 'ng-zorro-antd';

@Component({
  selector: 'app-upload-file',
  templateUrl: './upload-file.component.html',
  styleUrls: ['./upload-file.component.scss']
})
export class UploadFileComponent implements OnInit {

  public uploadInfos: Array<{
    index: number, // 自己的索引
    isLoad: boolean // true:读取文件
    isUpload: boolean // true:上传文件
    progress: number // 进度条
    status: any // 进度条状态
    md5: string // md5读取暂停
    stop: boolean // 分段上传暂停
    reader?: any // 文件读取器
    file?: any // 文件信息
    current?: number // 当前读取位置
    secondPass: boolean // 是否是秒传
    folders: Array<any> // 文件目录信息
    resultData: any, // 返回数据
    cancel: boolean // 是否关闭
    fail: boolean, // 上传失败
    res: any // 上传地址
  }>;
  public isSpinning: boolean;
  public currentObj: {
    successFun?: any
    length: number
    index: number
    folderId: string
  };
  public renewalSize; // 文件读取大小

  constructor(public uploadFileService: UploadFileService,
              public commonTool: CommonTool,
              private message: NzMessageService) {
    this.isSpinning = false;
    this.renewalSize = 10485760;
    this.uploadInfos = [];
    this.currentObj = {
      length: 0,
      index: 0,
      folderId: ''
    };
  }

  ngOnInit() {
  }


  public openHandle() {
    this.isSpinning = true;
  }

  public handleCancel() {
    this.isSpinning = false;
  }

  public handleOk() {
    this.isSpinning = false;
    for (const upload of this.uploadInfos) {
      upload.stop = true;
      upload.cancel = false;
    }
    this.uploadInfos = [];
    if (this.commonTool.notNull(this.currentObj.successFun)) {
      this.currentObj.successFun(false);
    }
  }

  /**
   * 重试
   */
  public retry(upload) {
    if (this.commonTool.notNull(upload.md5)) {
      upload.resultData['uploadUrl'] = upload.url;
      this.upload(upload.file, upload.res, upload.index);
    } else {
      upload.reader.readAsArrayBuffer(upload.file.slice(upload.current, upload.current + this.renewalSize));
    }
  }

  /**
   * 关闭
   */
  public cancel(upload) {
    upload.stop = true;
    upload.cancel = false;
    if (this.uploadInfos.length <= 0) {
      this.currentObj.successFun(true);
    }
  }

  /**
   * 暂停
   */
  public suspend(upload) {
    upload.stop = !upload.stop;
    if (this.commonTool.notNull(upload.md5)) {
      if (!upload.stop) {
        this.upload(upload.file, upload.resultData, upload.index);
      }
    } else {
      if (!upload.stop) {
        upload.reader.readAsArrayBuffer(upload.file.slice(upload.current, upload.current + this.renewalSize));
      }
    }
  }


  /**
   * 文件上传开始及初始化
   */
  public async uploadFileStart(files, folderId, successFun) {
    this.isSpinning = true;
    this.currentObj = {
      successFun,
      length: files.length,
      index: 0,
      folderId
    };
    let data = null;
    for (let i = 0; i < files.length; i++) {
      this.uploadInfos[i] = {
        index: i,
        isLoad: true,
        isUpload: false,
        progress: 0,
        status: 'active',
        md5: '',
        stop: false,
        reader: new FileReader(),
        file: files[i],
        current: 0,
        secondPass: false,
        folders: [],
        resultData: {},
        cancel: true,
        fail: false,
        res: {}
      };
      const file = this.uploadInfos[i].file;
      let array = [];
      if (file.webkitRelativePath !== '' && file.webkitRelativePath !== null && file.webkitRelativePath !== undefined) {
        array = files[i].webkitRelativePath.split('/');
        if (data === null) {
          data = {};
        }
        array.pop();
        this.uploadInfos[i].folders = array;
        data[files[i].webkitRelativePath] = {array, i};
      }
      const result: { md5, file, i, res } = await this.analysisMd5(files, i);
      this.uploadFile(result);
    }
  }

  /**
   * 解析md5
   */
  public analysisMd5(files, i) {
    const $this = this;
    return new Promise<{ md5, file, i, res }>(resolve => {
      const file = this.uploadInfos[i].file;
      // @ts-ignore
      const SparkMD5 = require('spark-md5');
      const reader = this.uploadInfos[i].reader;
      const spark = new SparkMD5.ArrayBuffer();
      let current = this.uploadInfos[i].current;
      reader.readAsArrayBuffer(file.slice(current, current + this.renewalSize));
      reader.onload = (e) => {
        if (!$this.uploadInfos[i] || $this.uploadInfos[i].stop) {
          return false;
        }
        spark.append(e.target['result']);
        if (file.size <= current) {
          const md5 = spark.end();
          this.uploadInfos[i].md5 = md5;
          this.uploadFileService.createFile({
            name: file.name,
            folderId: this.currentObj.folderId,
            folders: this.uploadInfos[i].folders,
            hashCode: md5,
            size: file.size
          }).subscribe((res) => {
            resolve({md5, file, i, res});
          });
        } else {
          current += $this.renewalSize;
          if (file.size <= current) {
            current = file.size;
          }
          reader.readAsArrayBuffer(file.slice(current, current + $this.renewalSize));
          if ($this.uploadInfos[i]) {
            const progress = (current / (file.size / 100));
            $this.uploadInfos[i].progress = parseInt(progress + '');
          }
        }
      };
    });
  }

  /**
   * 上传文件
   */
  public uploadFile(arr: { md5, file, i, res }) {
    this.uploadInfos[arr.i].res = arr.res;
    const res = arr.res;
    if (res['finish']) {
      this.uploadInfos[arr.i].secondPass = true;
      this.saveFile(arr.file.name, res['hashCode'], arr.file.size, arr.i);
    } else {
      this.upload(arr.file, res, arr.i);
    }
  }

  public upload(file, data, i) {
    const $this = this;
    // 上传文件
    const formData = new FormData();
    if (!this.commonTool.notNull(data['fileIndex'])) {
      data['fileIndex'] = 0;
    }
    const blob = file.slice(data['fileIndex'], data['fileIndex'] + parseInt(this.renewalSize));
    formData.append('file', blob);
    formData.append('uploadId', data['uploadId']);
    formData.append('hashCode', data['hashCode']);
    formData.append('fileIndex', data['fileIndex']);
    formData.append('size', file.size);
    const httpRequest = new XMLHttpRequest();
    httpRequest.open('POST', data['uploadUrl']);
    httpRequest.upload.addEventListener('progress', (evt) => {
      if ($this.renewalSize >= file.size) {
        const progress = Math.round((evt.loaded) * 100 / evt.total);
        $this.uploadInfos[i].progress = parseInt(progress + '');
        $this.uploadInfos[i].isLoad = false;
        $this.uploadInfos[i].isUpload = true;
        $this.uploadInfos[i].status = 'active';
      }
    }, false);
    httpRequest.onloadend = (res) => {
      if (!$this.uploadInfos[i] || $this.uploadInfos[i].stop) {
        return false;
      }
      if (res.target['status'] === 200) {
        if (res.target['response']) {
          const res1: { fileIndex; hashCode; finish; renewalSize; fid } = JSON.parse(res.target['response']);
          if (res1['finish']) {
            $this.saveFile(file.name, res1.hashCode, file.size, i);
          } else {
            data['fileIndex'] = res1['fileIndex'];
            const progress = Math.round((res1['fileIndex'] / file.size) * 100);
            $this.uploadInfos[i].progress = parseInt(progress + '');
            $this.uploadInfos[i].isLoad = false;
            $this.uploadInfos[i].fail = false;
            $this.uploadInfos[i].isUpload = true;
            $this.uploadInfos[i].status = 'active';
            $this.uploadInfos[i].resultData = data;
            $this.upload(file, data, i);
          }
        }
      } else if (res.target['status'] === 400) {
        $this.uploadInfos[i].status = 'exception';
        $this.uploadInfos[i].fail = true;
        if (res.target['response']) {
          const ref = JSON.parse(res.target['response']);
          console.error(ref);
          this.message.error(ref.message);
        }
      } else {
        $this.uploadInfos[i].status = 'exception';
        $this.uploadInfos[i].fail = true;
        return false;
      }
    };
    httpRequest.send(formData);
  }

  /**
   * 完成保存
   */
  public saveFile(name, hashCode, size, i) {
    this.currentObj.index++;
    this.uploadInfos[i].isLoad = false;
    this.uploadInfos[i].isUpload = true;
    this.uploadInfos[i].fail = false;
    this.uploadInfos[i].progress = 100;
    this.uploadInfos[i].status = '';
    setTimeout(() => {
      this.uploadInfos[i].cancel = false;
    }, 1000);
    if (this.currentObj.index === this.currentObj.length) {
      this.currentObj.successFun(true);
    }
  }
}
